/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package models;

import java.io.*;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import models.Tile;

/**
 *
 * @author dennis
 */
public class Level {
    
    private Tile[][] matrix;
    private Movable[][] movable;    
    
    private int xRange = 0;
    private int yRange = 0;
    
    private int tileWidth = 62;
    private int tileHeight = 62;
    
    private int countFinishTiles = 0;
    private int finished = 0;
    
    private int width;
    private int height;
    
    private AvatarMovable avatar;
    
    /**
     * Creates a new level without a tile matrix
     */
    public Level(){
        
        this.matrix = new Tile[0][0];
        
    }
    
    /**
     * Creates a new leven with a tile matrix
     * @param level tilematrix
     */
    public Level(String level){
        
        setTileMatrix(level);
                
    }
    
    /**
     * Gets the horizontal range of the level
     * @return x range
     */
    public int getRangeX(){
        return xRange;
    }
    
    /**
     * Gets the vertical range of the level
     * @return y range
     */
    public int getRangeY(){
        return yRange;
    }
    
    /**
     * Gets the tile matrix. This contains all the tile/movable objects. Array keys are the positions.
     * @return tile matrix
     */
    public Tile[][] getTileMatrix(){
        return this.matrix;
    }
    
    public void setTileMatrix(Tile[][] matrix){
        
        this.matrix = matrix;
        
        isFinished();
        
        for(int i = 0; i < matrix.length; i++){
            for(int j = 0; j < matrix[i].length; j++){ 

                if(matrix[i][j].hasMovable()){
                    Movable thisMovable = matrix[i][j].getMovable();
                    if(thisMovable instanceof AvatarMovable){
                        this.avatar = (AvatarMovable) thisMovable;
                    }
                }

            }
        }
        
    }
    
    /**
     * Sets the tile matrix. Input is a textfile with symbols.
     * @param levelTextFile representation of the level, this is a location to the textfile
     */
    public void setTileMatrix(String levelTextFile){
        
        HashMap<Integer, String> matrixStringLines = new HashMap<Integer, String>();
        
        FileInputStream fis;
        BufferedInputStream bis;
        DataInputStream dis;
        
        //find the file and create the File object for it.
        URL url = getClass().getResource("../levels/"+levelTextFile);
        File file = new File(url.getPath());
        
        try {
            
            fis = new FileInputStream(file);
            bis = new BufferedInputStream(fis);
            dis = new DataInputStream(bis);
            
            //Set lines in matrixStringLines HashMap and set the xRange and yRange
            while (dis.available() != 0) {
                matrixStringLines.put(yRange, dis.readLine());
                yRange++;
            }
            
            for(int row = 0; row < matrixStringLines.size(); row++){
                
                int length = matrixStringLines.get(0).length();
                if(length > xRange){
                    xRange = length;
                }
                //xRange = matrixStringLines.get(0).length();
            }
            
            //Now we know the size, lets set the matrix size;
            matrix = new Tile[xRange][yRange];
            movable = new Movable[xRange][yRange];
            
            //Loop through lines of StringLines
            for(int y = 0; y < matrixStringLines.size(); y++){
                
                char[] lineChars;
                
                String matrixLine = matrixStringLines.get(y);
                lineChars = matrixLine.toCharArray();
                
                int x = 0;
                for(char lineChar : lineChars ){
                    
                    switch(lineChar){
                        case '#': //wall tile
                            
                            matrix[x][y] = new WallTile(x, y, tileWidth, tileHeight);
                            
                            break;
                        case ' ': //road tile
                            
                            matrix[x][y] = new RoadTile(x, y, tileWidth, tileHeight);
                            
                            break;
                        case '$': //road tile & box movable
                            
                            movable[x][y] = new BoxMovable(x, y, false);
                            matrix[x][y] = new RoadTile(x, y, tileWidth, tileHeight, movable[x][y]);
                            
                            break;
                        case '.': //finish tile
                            
                            matrix[x][y] = new FinishTile(x, y, tileWidth, tileHeight);
                            incrementCountFinishTiles();
                            
                            break;
                        case '@': //road tile & avatar movable
                            
                            this.avatar = new AvatarMovable(x,y, false);
                            
                            movable[x][y] = this.avatar;
                            matrix[x][y] = new RoadTile(x, y, tileWidth, tileHeight, this.avatar);
                            
                            break;
                        case '*': //finish tile & box movable
                            
                            movable[x][y] = new BoxMovable(x, y, true);
                            matrix[x][y] = new FinishTile(x, y, tileWidth, tileHeight, movable[x][y]);
                            incrementCountFinishTiles();
                            
                            break;
                        case '+': //finish tile & avatar movable
                                                    
                            this.avatar = new AvatarMovable(x,y, true);
                            
                            movable[x][y] = this.avatar;
                            matrix[x][y] = new FinishTile(x, y, tileWidth, tileHeight, this.avatar);
                            incrementCountFinishTiles();
                            
                            break;
                    }
                    
                    
                    x++;
                }
                
                if(x < xRange){
                    for(int xCount = x; xCount < xRange; xCount++){
                        matrix[xCount][y] = new RoadTile(xCount, y, tileWidth, tileHeight);
                    }
                }
                
            }
            
            // dispose all the resources after using them.
            fis.close();
            bis.close();
            dis.close();
            
        } catch (FileNotFoundException e) {
            
            this.matrix = new Tile[0][0]; //set empty matrix
            
            //e.printStackTrace();
            
        } catch (IOException e) {
            
            this.matrix = new Tile[0][0]; //set empty matrix
            
            //e.printStackTrace();
            
        }
    }
    
    /**
     * Gets the avatar
     * @return avatar
     */
    public AvatarMovable getAvatar(){
        
        return this.avatar; 
        
    }
    
    /**
     * Calculates the dimensions of this level bases on x and y range, and tileheight and tilewidth
     */
    public void setDimensions(){
 
       this.height = (yRange * tileHeight) + 208;
       this.width  = (xRange * tileWidth) + 208;
        
    }
    
    /**
     * Gets the total height of this level
     * @return height of this level
     */
    public int getHeight(){
        return this.height;
    }
    
    /**
     * Gets the total width of this level
     * @return width of this level
     */
    public int getWidth(){
        return this.width;
    }
    
    /**
     * When building the tile matrix, we count the total amount of finish tiles, this increments it.
     */
    public void incrementCountFinishTiles(){
        countFinishTiles++;
    }
    
    /**
     * Gets the total amount of finish tiles
     * @return count of finish tiles
     */
    public int getCountFinishTiles(){
        return countFinishTiles;
    }
    
    /**
     * Gets the amount of boxes that are finished
     * @return amount of boxes that are finished
     */
    public int getCountFinished(){
        return finished;
    }
    

    /**
     * Loops through all the tiles and if tile is FinishTile, checks if is has a Movable, and if yes, check if it has a box on there
     * If all this is true for all FinishTiles, our game is finished
     * @return true or false (finished or not finished)
     */
    public boolean isFinished(){
        
        this.finished = 0;
        
        if(this.matrix.length > 0){
            for(int i = 0; i < this.matrix.length; i++){
                for(int j = 0; j < this.matrix[i].length; j++){

                    if(matrix[i][j] instanceof FinishTile){
                        if(matrix[i][j].hasMovable()){
                            if(matrix[i][j].getMovable() instanceof BoxMovable){
                                finished++;
                            }    
                        }
                    }
                    
                }
            }
        }
        
        if(finished == countFinishTiles)
            return true;
        else
            return false;
        
    }
    
}
